
#include "frpc_task.h"
#include <protocol/frpc_msgio.h>
#include <frpc/frpc.h>
#include <cppbase/utility.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>

namespace Frpc
{

    CommMessageOut *Task::message_out()
    {
        long long seqid = this->get_seq();

        if (seqid == 0)
        {
            DLOG("prepare to send username");
            is_user_request_ = false;
            auto msg = new RequestMsg;
            auto user = AppGlobal::instance()->get_config("username");
            if (user.size() == 0)
                user = "guest";
            //Args arg;
            //arg.add_arg_string(user);
            //arg.add_arg_int32(1);
            msg->set_cmd_output(Command::HandShake, std::move(user)); //arg.SerializeAsString());
            return msg;
        }
        if (seqid == 1)
        {
            DLOG("prepare to send hashed pw");
            is_user_request_ = false;
            auto msg = new RequestMsg;
            Context *ctx = (Context *)this->get_connection()->get_context();
            auto pw = AppGlobal::instance()->get_config("password");
            SHA256_CTX context;
            std::string hash_r, hash_r2;
            hash_r.resize(32);
            hash_r2.resize(32);
            SHA256_Init(&context);
            SHA256_Update(&context, (const unsigned char *)pw.data(), pw.size());
            SHA256_Update(&context, (unsigned char *)ctx->sec_salt.data(), ctx->sec_salt.size());
            SHA256_Final((unsigned char *)hash_r.data(), &context);
            SHA256_Init(&context);
            SHA256_Update(&context, (const unsigned char *)hash_r.data(), hash_r.size());
            SHA256_Final((unsigned char *)hash_r2.data(), &context);
            // Args arg;
            // arg.add_arg_string(std::move(hash_r2));
            msg->set_cmd_output(Command::LoginUser, std::move(hash_r2));
            return msg;
        }

        return this->WFComplexClientTask::message_out();
    }

    CommMessageIn *Task::message_in()
    {
        long long seqid = this->get_seq();
        //to build a handshake msg
        if (seqid < 2){
            is_user_request_=false;
            return new ReplyMsg;
            }
        return this->WFComplexClientTask::message_in();
    }

    bool Task::check_request()
    {
        return true;
    }
    //will be called after msg append. before call task callback
    //return 0 means close socket
    //to decide cmd result status
    int Task::keep_alive_timeout()
    {
        auto seqid = this->get_seq();
        state_ = WFT_STATE_SUCCESS;
        //error_ = 0;
        auto *resp = (RequestMsg *)this->get_message_in();
        if (resp->bad_input())
        {
            state_ = WFT_STATE_TASK_ERROR;
            return 0;
        }
        if (seqid < 2)
        {
            if (seqid == 0)
            {
                DLOG("set connection context with security salt.");
                auto *ctx = new Context;
                auto *conn = this->get_connection();
                ctx->sec_salt = resp->input();
                conn->set_context(ctx, [](void *ctx)
                                  { delete (Context *)ctx; });
            }
            else
            {
                DLOG("user login success.");
            }
        }

        return this->keep_alive_timeo; // in WFNetworkTask
    }
    //called after keep_alive_timeout
    bool Task::finish_once()
    {
        DLOG("call finish_once.");
        if (is_user_request_)
            return true;

        DLOG("not user request ,so delete out-in msg.");
        delete this->get_message_out();
        delete this->get_message_in();

        if (this->state == WFT_STATE_SUCCESS && state_ != WFT_STATE_SUCCESS)
        {
            this->state = state_;
            this->disable_retry();
        }
        is_user_request_=true;
        return false; //tell parent(WFComplexClientTask::done() ) don't call user callback
    }

    SubTask *ServerTask::done()
    {
        //	if (this->get_seq() == 0)
        //		delete this->get_message_in();

        return this->WFServerTask::done();
    }

    CommMessageOut *ServerTask::message_out()
    {
        //	long long seqid = this->get_seq();

        //if (seqid == 0)
        //	this->resp.set_ok_packet();	// always success
    DLOG("call ServerTask::message_out");
        return this->WFServerTask::message_out();
    }

    CommMessageIn *ServerTask::message_in()
    {
DLOG("call ServerTask::message_in");
        return this->WFServerTask::message_in();
    }

}